package com.zim.access.common.chat;

import com.zim.access.common.error.ErrorMessageConstants;
import com.zim.access.common.error.ErrorMessageFactory;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.NonNull;
import lombok.experimental.Accessors;

import java.io.*;
import java.util.Map;
import java.util.Objects;

/**
 * 消息模式
 *
 * @author hanyue
 * @version 1.0
 * @date 2021/12/20 下午11:37
 */
@Data
@Accessors(chain = true)
@NoArgsConstructor
public class Message<T> implements Cloneable {

    /**
     * UUID 保持唯一即可
     */
    private String id;

    /**
     * 有序 ID，由服务器保证会话内递增
     */
    private Long seqId;

    /**
     * 会话 ID
     */
    @NonNull
    private String sessionId;

    /**
     * 消息类型
     */
    @NonNull
    private Type type;

    /**
     * 消息内容
     */
    private T content;

    /**
     * 聊天
     */
    @NonNull
    private Chat chat;

    /**
     * 聊天来源方
     */
    private From from;

    /**
     * 聊天到达方
     */
    @NonNull
    private To to;

    /**
     * 消息送达模型
     */
    private Ack ack;

    /**
     * 消息已读模型
     */
    private Read read;

    /**
     * 消息创建时间
     */
    private Long timestamp;

    /**
     * 消息包袱，可以额外扩展自己所需属性
     */
    private Map<String, Object> baggage;

    /**
     * 消息是否已送达
     */
    public boolean hasAck() {
        return this.ack.timestamp != null;
    }

    /**
     * 消息是否已读
     */
    public boolean hasRead() {
        return this.read.timestamp != null;
    }

    /**
     * 深拷贝
     */
    @Override
    protected Object clone() {
        try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
             ObjectOutputStream oos = new ObjectOutputStream(bos)) {

            oos.writeObject(this);
            oos.flush();

            try (ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
                 ObjectInputStream ois = new ObjectInputStream(bis);) {

                return ois.readObject();
            } catch (IOException | ClassNotFoundException e) {
                return ErrorMessageFactory.INSTANCE.getObj(this, ErrorMessageConstants.CHAT_MSG_SERIALIZE_FAIL, e);
            }

        } catch (IOException e) {
            return ErrorMessageFactory.INSTANCE.getObj(this, ErrorMessageConstants.CHAT_MSG_SERIALIZE_FAIL, e);
        }
    }

    /**
     * 消息唯一性：消息 ID、消息序列号、会话 ID
     */
    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }

        if (o == null || getClass() != o.getClass()) {
            return false;
        }

        Message<?> message = (Message<?>) o;
        return Objects.equals(id, message.id) &&
                Objects.equals(seqId, message.seqId) &&
                Objects.equals(sessionId, message.sessionId);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, seqId, sessionId);
    }

    @Override
    public String toString() {
        final StringBuffer sb = new StringBuffer("Message{");
        sb.append("id='").append(id).append('\'');
        sb.append(", seqId=").append(seqId);
        sb.append(", type=").append(type);
        sb.append(", content=").append(content);
        sb.append(", chat=").append(chat);
        sb.append(", from=").append(from);
        sb.append(", to=").append(to);
        sb.append(", ack=").append(ack);
        sb.append(", read=").append(read);
        sb.append(", timestamp=").append(timestamp);
        sb.append(", baggage=").append(baggage);
        sb.append('}');
        return sb.toString();
    }

    @Data
    @Accessors(chain = true)
    @NoArgsConstructor
    public static class Chat {

        /**
         * 聊天类型
         */
        private ChatType type;
    }

    @Data
    @Accessors(chain = true)
    public static class From {

        /**
         * 用户 ID
         */
        private String id;

        /**
         * 消息从用户客户端发出时间，由客户端生成
         */
        private Long timestamp;
    }

    @Data
    @Accessors(chain = true)
    @NoArgsConstructor
    public static class To {

        /**
         * 用户 ID
         */
        private String id;

        /**
         * 消息从服务器投递给用户的时间，由服务器生成
         */
        private Long timestamp;
    }

    @Data
    @Accessors(chain = true)
    public static class Ack {

        /**
         * 消息已送达时间
         */
        private Long timestamp;
    }

    @Data
    @Accessors(chain = true)
    public static class Read {

        /**
         * 消息已读时间
         */
        private Long timestamp;
    }

    public enum Type {
        /**
         * 文本消息
         */
        TEXT(2),

        /**
         * 确认消息
         */
        ACK(3),

        /**
         * 已读消息
         */
        READ(4),

        /**
         * 心跳消息
         */
        HEARTBEAT(5),

        /**
         * 错误消息
         */
        ERROR(6);

        int value;

        Type(int value) {
            this.value = value;
        }
    }

    public enum ChatType {

        /**
         * 私聊
         */
        PRIVATE(0),

        /**
         * 群聊
         */
        GROUP(1),
        ;

        int value;

        ChatType(int value) {
            this.value = value;
        }
    }
}
